﻿using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Net;
using System.Text;

namespace TaskCompletionSourceDemo2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread.Sleep(1000);

            Start();

            Console.WriteLine("客户端Main函数结束");
            Console.ReadLine();
        }

        static async void Start()
        {
            Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} 开始");

            for (int i = 0; i < 5; i++) // 只能少量请求
            {
                int index = i;
                _ = Task.Run(async () =>
                {
                    MySocket mySocket = new MySocket();
                    mySocket.ConnectServer();
                    string result = await mySocket.SendReceiveAsync(index, $"服务端你好_{index}");
                    Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {result}");
                    mySocket.Close();
                });
            }

        }


    }

    internal class MySocket
    {
        public Socket _socket;

        private TaskCompletionSource<string> _tcs;

        public async Task<string> SendReceiveAsync(long index, string msg)
        {
            _tcs = new TaskCompletionSource<string>();
            if (_socket != null)
            {
                byte[] indexByteArr = BitConverter.GetBytes(index);
                byte[] msgByteArr = Encoding.UTF8.GetBytes(msg);
                int msgLength = msgByteArr.Length;
                byte[] msgLengthByteArr = BitConverter.GetBytes(msgLength);
                var bytes = new ArraySegment<byte>(indexByteArr.Concat(msgLengthByteArr).Concat(msgByteArr).ToArray());
                await _socket.SendAsync(bytes, SocketFlags.None);
            }
            return await _tcs.Task;
        }

        public void ConnectServer()
        {
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3001);
            _socket = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            _socket.SendTimeout = 20000;
            _socket.ReceiveTimeout = 20000;
            _socket.SendBufferSize = 10240;
            _socket.ReceiveBufferSize = 10240;

            _socket.Connect(ipep);

            byte[] buffer = new byte[10240];
            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.SetBuffer(buffer, 0, buffer.Length);
            args.Completed += (s, e) =>
            {
                if (s == null) return;

                ReceiveData(_socket, e);
            };
            _socket.ReceiveAsync(args);
        }

        private void ReceiveData(Socket socket, SocketAsyncEventArgs e)
        {
            if (e.BytesTransferred == 0)
            {
                // Console.WriteLine("e.BytesTransferred=0");
                return;
            }
            if (e.Buffer == null)
            {
                Console.WriteLine("e.Buffer=null");
                return;
            }
            if (e.BytesTransferred <= 12)
            {
                if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
            }

            byte[] buffer = new byte[e.BytesTransferred];
            CopyTo(e.Buffer, buffer, 0, 0, e.BytesTransferred);
            ProcessData(socket, e, buffer);
        }

        private void ProcessData(Socket socket, SocketAsyncEventArgs e, byte[] buffer)
        {
            byte[] longByteArr = new byte[8];
            byte[] msgLengthByteArr = new byte[4];
            CopyTo(buffer, longByteArr, 0, 0, 8);
            CopyTo(buffer, msgLengthByteArr, 8, 0, 4);
            long index = BitConverter.ToInt64(longByteArr);
            int msgLength = BitConverter.ToInt32(msgLengthByteArr);

            if (e != null && e.BytesTransferred < 12 + msgLength)
            {
                if (!socket.ReceiveAsync(e)) ReceiveData(socket, e);
            }

            string msg = Encoding.UTF8.GetString(buffer, 12, msgLength);
            if (_tcs != null)
            {
                _tcs?.SetResult(msg);
            }
        }

        private void CopyTo(byte[] bArrSource, byte[] bArrTarget, int sourceIndex, int targetIndex, int length)
        {
            for (int i = 0; i < length; i++)
            {
                if (targetIndex + i < bArrTarget.Length && sourceIndex + i < bArrSource.Length)
                {
                    bArrTarget[targetIndex + i] = bArrSource[sourceIndex + i];
                }
            }
        }

        public void Close()
        {
            _socket?.Close();
        }
    }
}
